package com.scorpio.sdk.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.scorpio.common.exception.CustomException;
import com.scorpio.common.utils.MathUtil;
import com.scorpio.common.utils.StringUtils;
import com.scorpio.common.utils.http.HttpUtils;
import com.scorpio.common.utils.http.Request;
import com.scorpio.common.utils.sign.Base64;
import com.scorpio.sdk.constant.WeiXinConstant;
import com.scorpio.sdk.dto.WXMiniPayPackge;
import com.scorpio.sdk.service.WXPayService;
import com.scorpio.system.service.ISysConfigService;
import com.sun.jndi.toolkit.url.UrlUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.crypto.IllegalBlockSizeException;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;

import static io.jsonwebtoken.lang.Strings.UTF_8;

@Service
public class WXPayServiceImpl implements WXPayService {

    @Value("${scorpio.serverIp}")
    private String serverIp;
    @Value("${wxpay.merchantPrivateKey}")
    private String merchantPrivateKey;

    @Autowired
    private ISysConfigService sysConfigService;

    private CloseableHttpClient httpClient;
    private AutoUpdateCertificatesVerifier verifier;
    private PrivateKey privateKey;
    private AesUtil aesUtil;


    @PostConstruct
    public void init() throws IOException {
        String mch_id = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_MCH_ID);
        String pay_secret = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_SECRET);
        String serialNumber = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_SERIAL_NUMBER);
        FileInputStream fis = new FileInputStream(merchantPrivateKey);
        aesUtil = new AesUtil(pay_secret.getBytes(StandardCharsets.UTF_8));
        privateKey = PemUtil.loadPrivateKey(fis);
        fis.close();
        //不需要传入微信支付证书了
        verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(mch_id, new PrivateKeySigner(serialNumber, privateKey)),
                pay_secret.getBytes(UTF_8));
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mch_id, serialNumber, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        httpClient = builder.build();
    }

    @Override
    public String encryptOAEP(String text) {
        // 建议从Verifier中获得微信支付平台证书，或使用预先下载到本地的平台证书文件中
        X509Certificate wechatpayCertificate = verifier.getValidCertificate();
        try {
            return RsaCryptoUtil.encryptOAEP(text, wechatpayCertificate);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        throw new CustomException("服务器异常");
    }


    /**
     * @param content 待签名数据
     * @return String 签名域
     * @throws
     * @Title: sign
     * @Description: RSA签名
     */
    @Override
    public String sign(String content) {
        try {
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(privateKey);
            signature.update(content.getBytes());
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new CustomException("服务器异常");
    }

    @Override
    public String decryptToString(String body) {
        JSONObject json = JSONObject.parseObject(body);
        JSONObject resource = json.getJSONObject("resource");
        String associated_data = resource.getString("associated_data");
        String nonce = resource.getString("nonce");
        String ciphertext = resource.getString("ciphertext");
        try {
            return aesUtil.decryptToString(associated_data.getBytes(UTF_8), nonce.getBytes(UTF_8), ciphertext);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void payClose(String orderSn) {
        String mch_id = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_MCH_ID);
        String serialNumber = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_SERIAL_NUMBER);
        String nonce_str = MathUtil.getRandLetter(MathUtil.NUMERIC, "", 32);
        String timestamp = "" + System.currentTimeMillis() / 1000;
        String path = String.format("/v3/pay/transactions/out-trade-no/%s/close", orderSn);

        JSONObject json = new JSONObject();
        json.put("mchid", mch_id);

        StringBuffer sb = new StringBuffer();
        sb.append("POST").append("\n")
                .append(path).append("\n")
                .append(timestamp).append("\n")
                .append(nonce_str).append("\n")
                .append(json.toJSONString()).append("\n")
        ;
        String signature = sign(sb.toString());
        sb.delete(0, sb.length());
        sb.append("WECHATPAY2-SHA256-RSA2048 ");
        sb.append("mchid=\"").append(mch_id).append("\",")
                .append("nonce_str=\"").append(nonce_str).append("\",")
                .append("signature=\"").append(signature).append("\",")
                .append("timestamp=\"").append(timestamp).append("\",")
                .append("serial_no=\"").append(serialNumber).append("\"")
        ;
        HashMap hander = new HashMap();
        hander.put("Authorization", sb.toString());

        try {
            String body = HttpUtils.request(
                    new Request()
                            .post()
                            .url(String.format(WeiXinConstant.WEIXIN_PAY_CLOSE_URL, orderSn))
                            .header(hander)
                            .body(json.toJSONString())
            );
            if ("204".equals(body)) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new CustomException("订单修改失败");
    }

    @Override
    public WXMiniPayPackge payJSAPICode(String orderSn, String openid, String goodsname, int totalFee) {
        String appid = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_APPID);
        String mch_id = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_MCH_ID);
        String notify_url = sysConfigService.selectConfigByKey(WeiXinConstant.WEIXIN_PAY_NOTIFY_URL);
        HttpPost httpPost = new HttpPost(WeiXinConstant.WEIXIN_PAY_JPAPI_URL);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        JSONObject amount = new JSONObject();
        amount.put("total", totalFee);
        JSONObject payer = new JSONObject();
        payer.put("openid", openid);

        JSONObject json = new JSONObject();
        json.put("mchid", mch_id);
        json.put("appid", appid);
        json.put("description", goodsname);
        json.put("notify_url", notify_url);
        json.put("out_trade_no", orderSn);
        json.put("amount", amount);
        json.put("payer", payer);

        httpPost.setEntity(new StringEntity(json.toJSONString(), UTF_8));
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject result = JSONObject.parseObject(bodyAsString);
            String code = result.getString("code");
            if (!StringUtils.isEmpty(code)) {
                String message = result.getString("message");
                throw new CustomException(message);
            } else {
                String prepay_id = result.getString("prepay_id");
                if (!StringUtils.isEmpty(prepay_id)) {
                    return WeiXinConstant.getMiniPaySignJson(appid, prepay_id);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new CustomException("下单失败");
    }

}
